<?php

/**
 * RaiseForm UIControl class
 * Note that in order for the field labels and form fields to be added into the
 * template, the template must include tokens $label$ and $field$ in the template.
 *
 * @author Sam Yong
 * @license http://www.opensource.org/licenses/bsd-license New BSD License
 * @package Raise.Core.UI.Form
 * @since 1.2
 */
class RaiseForm extends RaiseUIControl {

    /**
     * The session key to keep track of the all fields and validators
     */
    const SESSION_KEY = '_?:RaiseForm';

    /**
     *
     */
    const VALIDATOR_MAPPER_FIELD = 'inyvqng0eZ4ccre';

    /**
     * The RaiseApplication that started this RaisePostback
     * @var RaiseApplication
     */
    private $application;

    /**
     * The URL route of the postback controller which this form will send its data to when submitted.
     * @var RaiseUrlRoute
     */
    private $postback;

    /**
     * Collection of labels or fields in the form
     * @var RaiseCollection
     */
    private $fields;

    /**
     * The template used to repeatively display the form controls
     * @var RaiseTemplate
     */
    private $template;

    /**
     * The method to use, i.e. POST or GET
     * @var string
     */
    private $method = 'post';

    /**
     * Create a new RaiseForm
     * @param RaiseTemplate $t (optional) The template that will be used to display the form
     */
    public function  __construct($t = null, $m = 'post') {
        $this->template($t);
        $this->method = $post;
    }

    /**
     * Get or set the RaiseApplication that started this postback controller
     * @param RaiseApplication $app (optional) If set, the new value will be set.
     * @return RaiseApplication
     */
    public function application($app = null){
        if(func_num_args() == 1){
            if(!($app instanceof RaiseApplication)){
                throw new RaiseInvalidArgumentException('RaiseForm::application() expecting $app to be an instance of RaiseApplication, ' . RaiseVariable::typeInfo($app) . ' given instead.');
            }
            $this->application = $app;
        }
        return $this->application;
    }
    
    /**
     * Get or set $description$
     * @param string $p (optional) $description$
     * @return string
     */
    public function postback($p = null){
        if(func_num_args() == 1){
            $this->postback = $p;
        }
        return $this->postback;
    }

    /**
     * Get or set $description$
     * @param RaiseCollection $f (optional) $description$
     * @return RaiseCollection
     */
    public function fields($f = null){
        if(func_num_args() == 1){
            $this->fields = $f;
        }
        return $this->fields;
    }

    /**
     * Get or set the template to use for each item of the repeater
     * @param RaiseTemplate $t (Optional) The template to set
     * @return RaiseTemplate
     */
    public function template($t = null){
        if(func_num_args() == 1){
            if(!($t instanceof RaiseTemplate)){
                throw new RaiseInvalidArgumentException('RaiseForm::template expecting RaiseTemplate as first parameter; ' . RaiseVariable::typeInfo($t) . ' was given instead.');
            }
            $this->template = $t;
        }
        return $this->template;
    }

    /**
     * Get or set method of the form (i.e. post or get)
     * @param string $m (optional) If entered, the new value will be set
     * @return string
     */
    public function method($m = null){
        if(func_num_args() == 1){
            $this->method = $m;
        }
        return $this->method;
    }

    public function output() {
        $repeater = new RaiseRepeater($this->template);
        $repeater->data()->columns()->add('label');
        $repeater->data()->columns()->add('field');
        $file = false;
        $validatorMapping = new RaiseCollection();
        foreach($this->fields as $f){
            if($f instanceof RaiseFormField){
                $a = array('label' => '', 'field' => $f->output());
                if($f->label() != null){
                    $a['label'] = $f->label()->output();
                }
                if($f instanceof RaiseFileUploadField){
                    $file = true;
                }
                if($f->validator() != null){
                    if($f->validator() instanceof RaiseValidator){
                        $validatorMapping->add($f->clientId(), $f->validator());
                    }
                }
                $repeater->data()->add($a);
            }elseif($f instanceof RaiseFieldLabel){
                $a = array('label' => $f->output(), 'field' => '');
                if($f->field() != null){
                    $a['field'] = $f->field()->output();
                    if($f->field() instanceof RaiseFileUploadField){
                        $file = true;
                    }
                    if($f->field()->validator() != null){
                        if($f->field()->validator() instanceof RaiseValidator){
                            $validatorMapping->add($f->field()->clientId(), $f->field()->validator());
                        }
                    }
                }
                $repeater->data()->add($a);
            }
        }

        $mapper = null;
        if($validatorMapping->count() > 0){
            $mappings = $this->application->session()->get(self::SESSION_KEY);
            if(!is_array($mappings)){
                $mappings = array();
            }
            $mappings[$this->clientId()] = $validatorMapping;
            $this->application->session()->set(self::SESSION_KEY, $mappings);

            $mapper = new RaiseHtmlTag('input', $this->clientId(), array('type' => 'hidden', 'name' => self::VALIDATOR_MAPPER_FIELD));
        }
        $tag = new RaiseHtmlTag('form', $repeater->output(), array('action' => '', 'method' => $this->method, 'id' => $this->clientId));
        if($mapper instanceof RaiseHtmlTag){
            $tag->value($mapper->html().$tag->value());
        }
        if($file){
            $tag->attributes()->add('enctype', 'multipart/form-data');
        }
        return $tag->html();
    }

}
